﻿using System;
using System.Data;
using NHibernate;

namespace MoroMoro.Samples.ToDoList.Base.Persistence.NHibernate
{
    /// <summary>
    /// NHibernate を使用して実装される作業スペースです。
    /// </summary>
    /// <seealso cref="IWorkspace"/>
    /// <seealso cref="NRepository&lt;TEntity&gt;"/>
    [Serializable]
    internal sealed class NWorkspace : IWorkspace
    {
        /// <summary>
        /// <see cref="NWorkspace"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="sessionFactory">NHibernate セッションファクトリ。</param>
        /// <exception cref="System.ArgumentNullException">sessionFactory が null です。</exception>
        public NWorkspace(ISessionFactory sessionFactory)
        {
            if (sessionFactory == null)
            {
                throw new ArgumentNullException("sessionFactory");
            }
            _sessionFactory = sessionFactory;
        }


        /// <summary>
        /// NHibernate セッションファクトリ。
        /// </summary>
        private readonly ISessionFactory _sessionFactory;

        /// <summary>
        /// NHibernate セッション。
        /// </summary>
        private ISession _session;


        /// <summary>
        /// NHibernate セッションを取得します。
        /// </summary>
        private ISession Session
        {
            get
            {
                if (_session == null)
                {
                    _session = _sessionFactory.OpenSession();
                    _session.FlushMode = FlushMode.Never;
                }
                return _session;
            }
        }


        /// <inheritdoc/>
        public IRepository<TEntity> GetRepository<TEntity>()
            where TEntity : class
        {
            return new NRepository<TEntity>(Session);
        }

        /// <inheritdoc/>
        public void Clear()
        {
            if (_session != null)
            {
                _session.Dispose();
                _session = null;
            }
        }

        /// <inheritdoc/>
        public void PersistAll(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
        {
            using (var tran = Session.BeginTransaction(isolationLevel))
            {
                try
                {
                    Session.Flush();
                    tran.Commit();
                }
                catch (StaleObjectStateException ex)
                {
                    tran.Rollback();
                    Session.Dispose();
                    throw new ConcurrencyException(ex);
                }
                catch
                {
                    tran.Rollback();
                    Session.Dispose();
                    throw;
                }
            }
        }

        /// <inheritdoc/>
        public bool IsDirty()
        {
            return Session.IsDirty();
        }


        /// <summary>
        /// リソースを解放します。
        /// </summary>
        public void Dispose()
        {
            if (_session != null)
            {
                _session.Dispose();
            }
        }
    }
}
